PhpExtension   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 90
dl 0
loc 122
rs 10
c 0
b 0
f 0
wmc 14

7 Functions

Rating   Name   Duplication   Size   Complexity  
A disable 0 12 1
A isInstalled 0 3 1
A getPhpIni 0 15 3
A uninstall 0 2 1
A isEnabled 0 5 1
A enable 0 15 2
B install 0 25 5
1
import execa from 'execa'
2
import {existsSync} from 'fs'
3
import * as fs from 'fs'
4
import {success, warning} from '../utils/console'
5
6
abstract class PhpExtension {
7
    static NORMAL_EXTENSION_TYPE = 'extension'
8
    static ZEND_EXTENSION_TYPE = 'zend_extension'
9
10
    abstract extension: string
11
    abstract alias: string
12
13
    // Extension settings
14
    default = true
15
    extensionType: string = PhpExtension.NORMAL_EXTENSION_TYPE
16
17
    /**
18
     * Get the path of the PHP ini currently used by PECL.
19
     */
20
    getPhpIni = async (): Promise<string> => {
21
        const peclIni = await execa('pecl', ['config-get', 'php_ini'])
22
        const peclIniPath = peclIni.stdout.replace('\n', '')
23
24
        if (existsSync(peclIniPath))
25
            return peclIniPath
26
27
        const phpIni = await execa('php', ['-i', '|', 'grep', 'php.ini'])
28
29
        const matches = phpIni.stdout.match(/Path => ([^\s]*)/)
30
31
        if (!matches || matches.length <= 0)
32
            throw new Error('Unable to find php.ini.')
33
34
        return `${matches[1].trim()}/php.ini`
35
    }
36
37
    /**
38
     * Check if the extension is enabled.
39
     */
40
    isEnabled = async (): Promise<boolean> => {
41
        const {stdout} = await execa('php', ['-m', '|', 'grep', this.extension])
42
        const extensions = stdout.split('\n')
43
44
        return extensions.includes(this.extension)
45
    }
46
47
    /**
48
     * Check if the extension is installed.
49
     */
50
    isInstalled = async (): Promise<boolean> => {
51
        const {stdout} = await execa('pecl', ['list', '|', 'grep', this.extension])
52
        return stdout.includes(this.extension)
53
    }
54
55
    /**
56
     * Install the extension.
57
     */
58
    install = async (): Promise<boolean> => {
59
        if (await this.isInstalled()) {
60
            warning(`Extension ${this.extension} is already installed.`)
61
            return false
62
        }
63
64
        const {stdout} = await execa('pecl', ['install', this.extension])
65
66
        const installRegex = new RegExp(`Installing '(.*${this.alias}.so)'`, 'g').test(stdout)
67
        if (!installRegex)
68
            throw new Error(`Unable to find installation path for ${this.extension}. Result:\n\n`)
69
70
        if (stdout.includes('Error:'))
71
            throw new Error(`Found installation path, but installation still failed: \n\n${stdout}`)
72
73
        const phpIniPath = await this.getPhpIni()
74
        const phpIni = await fs.readFileSync(phpIniPath, 'utf-8')
75
76
        // TODO: Fix duplicate extension entires in php.ini
77
        const extensionRegex = new RegExp(`(zend_extension|extension)="(.*${this.alias}.so)"`, 'g').test(phpIni)
78
        if (!extensionRegex)
79
            throw new Error(`Unable to find definition in ${phpIniPath} for ${this.extension}`)
80
81
        success(`Extension ${this.extension} has been installed.`)
82
        return true
83
    }
84
85
    /**
86
     * Uninstall the extension.
87
     */
88
    uninstall = async (): Promise<void> => {
89
        await execa('pecl', ['uninstall', this.extension])
90
    }
91
92
    /**
93
     * Enable the extension.
94
     */
95
    enable = async (): Promise<void> => {
96
        if (await this.isEnabled()) {
97
            warning(`Extension ${this.extension} is already enabled.`)
98
            return
99
        }
100
101
        const phpIniPath = await this.getPhpIni()
102
        let phpIni = await fs.readFileSync(phpIniPath, 'utf-8')
103
        const regex = new RegExp(`(zend_extension|extension)="(.*${this.alias}.so)"\\n`, 'g')
104
        phpIni = phpIni.replace(regex, '')
105
        phpIni = `${this.extensionType}="${this.alias}.so"\n${phpIni}`
106
107
        await fs.writeFileSync(phpIniPath, phpIni)
108
109
        success(`Extension ${this.extension} has been enabled.`)
110
    }
111
112
    /**
113
     * Disable the extension.
114
     */
115
    disable = async (): Promise<boolean> => {
116
        const phpIniPath = await this.getPhpIni()
117
        let phpIni = await fs.readFileSync(phpIniPath, 'utf-8')
118
119
        const regex = new RegExp(`;?(zend_extension|extension)=".*${this.alias}.so"\n`, 'g')
120
        phpIni = phpIni.replace(regex, '')
121
122
        await fs.writeFileSync(phpIniPath, phpIni)
123
124
        success(`Extension ${this.extension} has been disabled.`)
125
126
        return true
127
    }
128
}
129
130
export default PhpExtension